﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace Hsenl.Network {
    // 流量监控. 每个channel发送与接收了多少数据
    public class TrafficMonitoringPlug : IPlug, IOnChannelStarted, IOnChannelDisconnected, IAfterMessageReaded, IAfterMessageWrited {
        private Service _service;
        private readonly ConcurrentDictionary<long, Box> _boxes = new();

        private bool _isInited;

        public void Init(IPluggable pluggable) {
            if (this._isInited)
                return;

            this._isInited = true;

            this._service = (Service)pluggable;
            Coroutine.Start(this.StatisticalThroughput());
        }

        public void Dispose() {
            this._boxes.Clear();
        }

        void IOnChannelStarted.Handle(long channelId) {
            this.RemoveBox(channelId);
            var box = new Box();
            this._boxes.TryAdd(channelId, box);
        }

        void IOnChannelDisconnected.Handle(long channelId, int error) {
            this.RemoveBox(channelId);
        }

        void IAfterMessageReaded.Handle(long channel, Memory<byte> data) {
            if (this._boxes.TryGetValue(channel, out var box)) {
                box.AddRecv(data.Length);
                box.AddRecvCount(1);
            }
        }

        void IAfterMessageWrited.Handle(long channel, Memory<byte> data) {
            if (this._boxes.TryGetValue(channel, out var box)) {
                box.AddSend(data.Length);
                box.AddSendCount(1);
            }
        }

        public bool GetTraffic(long channelId, out (int tbr, int tbs) result) {
            if (this._boxes.TryGetValue(channelId, out var box)) {
                result = (box.TotalBytesRecv, box.TotalBytesSend);
                return true;
            }

            result = default;
            return false;
        }

        private void RemoveBox(long channelId) {
            if (this._boxes.TryRemove(channelId, out var box))
                box.Dispose();
        }

        // 统计吞吐量
        IEnumerator StatisticalThroughput() {
            var waiter = new WaitSeconds(1000);
            while (true) {
                var recvdata1 = 0;
                var recvcount1 = 0;
                var sendata1 = 0;
                var sendcount1 = 0;
                foreach (var kv in this._boxes) {
                    var box = kv.Value;
                    recvdata1 += box.TotalBytesRecv;
                    recvcount1 += box.TotalCountRecv;
                    sendata1 += box.TotalBytesSend;
                    sendcount1 += box.TotalCountSend;
                }

                yield return waiter;
                waiter.Reset(1000);

                var recvdata2 = 0;
                var recvcount2 = 0;
                var sendata2 = 0;
                var sendcount2 = 0;
                foreach (var kv in this._boxes) {
                    var box = kv.Value;
                    recvdata2 += box.TotalBytesRecv;
                    recvcount2 += box.TotalCountRecv;
                    sendata2 += box.TotalBytesSend;
                    sendcount2 += box.TotalCountSend;
                }

                var diff1 = recvdata2 - recvdata1;
                var diff2 = recvcount2 - recvcount1;
                var diff3 = sendata2 - sendata1;
                var diff4 = sendcount2 - sendcount1;
                if (diff1 > 0 || diff2 > 0 || diff3 > 0 || diff4 > 0)
                    Log.Info($"服务端吞吐量----接收: {(diff1 / 1024f):f1} KB/s, {diff2} 次/s----发送: {(diff3 / 1024f):f1} KB/s, {diff4} 次/s");
            }
        }

        private class Box {
            private volatile int _totalBytesRecv;
            private volatile int _totalBytesSend;
            private volatile int _totalCountRecv;
            private volatile int _totalCountSend;

            public int TotalBytesRecv => this._totalBytesRecv;

            public int TotalBytesSend => this._totalBytesSend;

            public int TotalCountRecv => this._totalCountRecv;

            public int TotalCountSend => this._totalCountSend;

            public void AddRecv(int value) {
                Interlocked.Add(ref this._totalBytesRecv, value);
            }

            public void AddSend(int value) {
                Interlocked.Add(ref this._totalBytesSend, value);
            }

            public void AddRecvCount(int value) {
                Interlocked.Add(ref this._totalCountRecv, value);
            }

            public void AddSendCount(int value) {
                Interlocked.Add(ref this._totalCountSend, value);
            }

            public void Dispose() {
                this._totalBytesRecv = 0;
                this._totalBytesSend = 0;
                this._totalCountRecv = 0;
                this._totalCountSend = 0;
            }
        }
    }
}